package com.aegis.gateway.handler;

import cn.hutool.json.JSONUtil;
import com.aegis.common.exception.AuthorizationException;
import com.aegis.common.exception.BaseException;
import com.aegis.common.web.BaseResponseEnum;
import com.aegis.common.web.IResponseEnum;
import com.aegis.common.web.ResultUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsConfigurationSource;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

/**
 * @Author renogo
 *
 * @Version 1.0
 * @Descritube 异常会跳出过滤器的执行链，被这里捕获。SA的异常也会跳出过滤器执行链，由其SA自己处理，当前类不参与。
 */
@Slf4j
@Order(-1)
@Configuration
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class GlobalErrorWebExceptionHandler implements ErrorWebExceptionHandler {


    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        log.error("网关异常捕获 {}", JSONUtil.toJsonStr(exchange.getRequest()), ex);
        ServerHttpResponse response = exchange.getResponse();

        if (response.isCommitted()) {
            return Mono.error(ex);
        }

        // 设置返回值类型为json
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

        Integer code = BaseResponseEnum.OPERATE_FAIL.getCode();
        String message = BaseResponseEnum.OPERATE_FAIL.getMessage();
        //非200状态码错误，如404
        if (ex instanceof ResponseStatusException) {
            HttpStatus status = ((ResponseStatusException) ex).getStatus();
            message = status.getReasonPhrase();
            code = status.value();
            response.setStatusCode(((ResponseStatusException) ex).getStatus());
        } else if (ex instanceof BaseException) {
            BaseException baseException = ((BaseException) ex);
            message = (ex).getMessage();
            code = baseException.getCode();
            Integer httpStatusCode = baseException.getHttpStatusCode();
            if (httpStatusCode == null) {
                httpStatusCode = HttpStatus.OK.value();
            }
            HttpStatus httpStatus = HttpStatus.resolve(httpStatusCode);
            response.setStatusCode(httpStatus == null ? HttpStatus.OK : httpStatus);
        }

        response.getHeaders().set("Access-Control-Allow-Origin", "*");
        response.getHeaders().set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.getHeaders().set("Access-Control-Allow-Headers", "*");
        response.getHeaders().set("Access-Control-Allow-Credentials", "false");

        final Integer finalCode = code;
        final String finalMessage = message;
        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            try {
                return bufferFactory.wrap(JSONUtil.toJsonStr(ResultUtil
                        .genCommonResult(finalCode, finalMessage)).getBytes(StandardCharsets.UTF_8));
            } catch (Exception e) {
                log.error("Error writing response", ex);
                return bufferFactory.wrap(new byte[0]);
            }
        }));
    }
}
